﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Justin.FrameWork.Helper;
using Justin.FrameWork.WinForm.Helper;
using Justin.Log;
using Justin.Stock.DAL;
using Justin.Stock.Entities;
using Justin.Stock.Extensions;
using Justin.Stock.Models;

namespace Justin.Stock
{
    //delegate void ScreenMessage(TableLayoutPanel tableLayoutPanel1, StringBuilder stocksMessage);
    //delegate void NotifyMessage(string message);
    delegate void FormInvoke(FormInvokArgument argument);
    public partial class DeskStocks : Form
    {
        #region 私有变量

        HotkeyHelper hotkeyHelper;
        int showWindowKey;
        int showWarnKey;
        string fileName;
        bool forceClose = false;

        bool initShow = true;
        bool IsShow = false;//当前是否显示窗体
        MyStock myStock = null; //实时监控
        StockNotify notify = new StockNotify();//通知

        #endregion

        #region 窗体加载、关闭事件

        public DeskStocks(string fileName)
        {
            InitializeComponent();
            this.MouseWheel += new MouseEventHandler(DeskStocks_MouseWheel);
            this.fileName = fileName;
        }

        private void DeskStocks_Load(object sender, EventArgs e)
        {
            #region 股票无关

            LoadLastFormPosition();
            RegisterHotKey();

            #endregion
            StockService.AddEvent(Display);
            StockService.AddEvent(ShowWarn);

            StockService.Start();
            myStock = new MyStock(this);


        }

        private void DeskStocks_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!forceClose)
            {
                e.Cancel = true;
                this.Hide();
            }
        }

        #endregion

        #region 函数
        public void Display(object sender, StockEventArgs e)
        {

            IEnumerable<StockInfo> StockList = e.Stocks;
            try
            {
                #region format

                string notifyFormat = "{0}:{1}{2}%{6}%";
                string tipsFormat =
@"{0} 涨:{9}% 换:{14}% 盈:{11}
现价：{3} 成本：{13} 股:{12} 
最高：{4} 最低：{5}
今开:{1} 昨收：{2} 
成交量/额:{6}手/{7}万元
买：          卖：
{15}
时间：{8}
当前：{10}";

                string fiveDealFormat =
@"{0}:{1}   {2}:{3}";

                #endregion

                #region 桌面控件初始化

                TableLayoutPanel tableLayoutPanel1 = new TableLayoutPanel();

                var rowStyle = new RowStyle(SizeType.Absolute, 9);

                for (int j = 0; j < 50; j++)
                {
                    tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Absolute, 10));
                }
                tableLayoutPanel1.ColumnCount = 1;
                tableLayoutPanel1.RowCount = StockList.Count();
                tableLayoutPanel1.Height = tableLayoutPanel1.RowCount * 10;
                tableLayoutPanel1.Dock = DockStyle.Fill;
                List<Control> controls = new List<Control>();
                foreach (Control item in tableLayoutPanel1.Controls)
                {
                    controls.Add(item);
                }
                foreach (var item in controls)
                {
                    item.Dispose();
                }
                tableLayoutPanel1.Controls.Clear();

                #endregion

                int i = 0;
                StringBuilder stocksMessageBuilder = new StringBuilder();
                ToolTip tip = new ToolTip();
                var stocks = StockList.Where(row => row.ShowInFolatWindow).OrderByDescending(row => row.BuyCount);
                foreach (var rtStock in stocks)
                {
                    Label message = new Label();

                    #region 股票桌面信息

                    message.ContextMenuStrip = deskMenu;
                    message.Tag = rtStock.Code;
                    message.Width = 200;
                    message.Font = new Font("Consolas", 8F, System.Drawing.FontStyle.Regular);
                    message.Text = string.Format(Constants.DeskDisplayFormat
                                              , rtStock.SpellingInShort.PadLeft(4, ' ')
                                              , Math.Round(rtStock.PriceNow, 2).ToString().PadLeft(7, ' ')
                                              , rtStock.SurgedRange.ToString().PadLeft(6, ' ')
                                             , rtStock.ProfitOrLoss.ToString().PadLeft(5, ' ')
                                             , rtStock.BuyPrice.ToString().PadLeft(7, ' ')
                                             , rtStock.BuyCount.ToString().PadLeft(5, ' ')
                                             , rtStock.TurnOver.ToString().PadLeft(6, ' ')
                                             , rtStock.MarketValue.ToString().PadLeft(6, ' ')
                                              );
                    string stockMessage = string.Format(notifyFormat
                                             , rtStock.SpellingInShort
                                             , Math.Round(rtStock.PriceNow, 2).ToString().PadLeft(7, ' ')
                                             , rtStock.SurgedRange.ToString().PadLeft(6, ' ')
                                            , rtStock.ProfitOrLoss.ToString().PadLeft(5, ' ')
                                            , rtStock.BuyPrice.ToString().PadLeft(7, ' ')
                                            , rtStock.BuyCount.ToString().PadLeft(5, ' ')
                                            , rtStock.TurnOver.ToString().PadLeft(6, ' ')
                                             );
                    stocksMessageBuilder.Append(stockMessage).AppendLine();
                    message.Dock = DockStyle.Fill;

                    #endregion

                    #region 股票提示信息

                    string fiveDeal = new StringBuilder()
                        .AppendFormat(fiveDealFormat, rtStock.Buy1Price.ToString().PadLeft(5, ' '), rtStock.Buy1Count.ToString().PadLeft(6, ' '), rtStock.Sell1Price.ToString().PadLeft(5, ' '), rtStock.Sell1Count.ToString().PadLeft(6, ' ')).AppendLine()
                        .AppendFormat(fiveDealFormat, rtStock.Buy2Price.ToString().PadLeft(5, ' '), rtStock.Buy2Count.ToString().PadLeft(6, ' '), rtStock.Sell2Price.ToString().PadLeft(5, ' '), rtStock.Sell2Count.ToString().PadLeft(6, ' ')).AppendLine()
                        .AppendFormat(fiveDealFormat, rtStock.Buy3Price.ToString().PadLeft(5, ' '), rtStock.Buy3Count.ToString().PadLeft(6, ' '), rtStock.Sell3Price.ToString().PadLeft(5, ' '), rtStock.Sell3Count.ToString().PadLeft(6, ' ')).AppendLine()
                        .AppendFormat(fiveDealFormat, rtStock.Buy4Price.ToString().PadLeft(5, ' '), rtStock.Buy4Count.ToString().PadLeft(6, ' '), rtStock.Sell4Price.ToString().PadLeft(5, ' '), rtStock.Sell4Count.ToString().PadLeft(6, ' ')).AppendLine()
                        .AppendFormat(fiveDealFormat, rtStock.Buy5Price.ToString().PadLeft(5, ' '), rtStock.Buy5Count.ToString().PadLeft(6, ' '), rtStock.Sell5Price.ToString().PadLeft(5, ' '), rtStock.Sell5Count.ToString().PadLeft(6, ' ')).AppendLine()
                        .ToString();
                    string tips = string.Format(tipsFormat
                        , rtStock.Name
                         , rtStock.PriceTodayStart
                         , rtStock.PriceYesterdayEnd
                         , rtStock.PriceNow
                         , rtStock.PriceTodayHigh
                         , rtStock.PriceTodayLow
                         , rtStock.DealsStockAmt
                         , rtStock.DealsMoney
                         , rtStock.DateTime
                         , rtStock.SurgedRange
                         , rtStock.Now
                         , rtStock.ProfitOrLoss
                         , rtStock.BuyCount
                         , rtStock.BuyPrice
                         , rtStock.TurnOver
                         , fiveDeal
                        );

                    tip.SetToolTip(message, tips);

                    #endregion

                    tableLayoutPanel1.Controls.Add(message, 0, i);
                    i++;
                }

                #region 总盈亏信息

                decimal sumCost = stocks.Where(row => row.BuyCount > 0).Sum(row => row.BuyCount * row.BuyPrice) + Constants.Balance;//成本
                decimal sumPrice = stocks.Where(row => row.BuyCount > 0).Sum(row => row.MarketValue) + Constants.Balance;//市值

                tableLayoutPanel1.Tag = string.Format("{0}/{1}:{2}", (int)sumCost, (int)sumPrice, (int)(sumPrice - sumCost));

                #endregion

                #region 实时显示股票信息到桌面

                FormInvokArgument argument = new FormInvokArgument()
                {
                    tableLayoutPanel1 = tableLayoutPanel1,
                };

                if (this.InvokeRequired == true)
                {
                    this.Invoke(new FormInvoke(ShowStockInDesk), argument);
                }
                else
                {
                    ShowStockInDesk(argument);
                }

                #endregion

            }
            catch (Exception ex)
            {
                JLog.Write(LogMode.Error, ex);
            }

        }
        private void ShowStockInDesk(FormInvokArgument argument)
        {
            TableLayoutPanel tableLayoutPanel1 = argument.tableLayoutPanel1;
            List<Control> controls = new List<Control>();
            foreach (Control item in this.Controls)
            {
                controls.Add(item);
            }
            foreach (var item in controls)
            {
                item.Dispose();
            }
            this.Text = tableLayoutPanel1.Tag.ToString();
            this.Controls.Clear();
            this.Controls.Add(tableLayoutPanel1);

            this.notifyIcon1.SetText(tableLayoutPanel1.Tag.ToString());
            if (initShow)
            {
                this.Hide();
                this.initShow = !this.initShow;
            }
        }

        int WarnMultiple = 3;
        int timer = 0;
        private void ShowWarn(object sender, StockEventArgs e)
        {
            IEnumerable<StockInfo> StockList = e.Stocks;
            if (Constants.ShowWarn)
            {
                timer++;
                if (timer % WarnMultiple != 0)
                {
                    return;
                }

                if (StockList == null)
                {
                    return;
                }
                string str = "";
                foreach (var stock in StockList)
                {
                    bool showWarn = false;
                    if (stock.PriceNow != 0)
                    {
                        if (stock.WarnPrice_Max != 0 && stock.PriceNow > stock.WarnPrice_Max)
                        {
                            showWarn = true;
                        }
                        if (stock.WarnPrice_Min != 0 && stock.PriceNow < stock.WarnPrice_Min)
                        {
                            showWarn = true;
                        }
                        if (stock.WarnPercent_Max != 0 && stock.SurgedRange > stock.WarnPercent_Max)
                        {
                            showWarn = true;
                        }
                        if (stock.WarnPercent_Min != 0 && stock.SurgedRange < stock.WarnPercent_Min)
                        {
                            showWarn = true;
                        }

                        if (showWarn)
                            str += stock.SpellingInShort + ";";
                    }
                }
                FormInvokArgument argument = new FormInvokArgument() { Message = str };
                if (this.InvokeRequired == true)
                {
                    this.Invoke(new FormInvoke(DisplayWarn), argument);
                }
                else
                {
                    DisplayWarn(argument);
                }
            }

        }
        private void DisplayWarn(FormInvokArgument arg)
        {
            if (arg.Message.Length > 0)
            {
                if (notify.IsDisposed)
                {
                    notify = new StockNotify();
                }
                notify.Show(arg.Message);
                //this.notifyIcon1.ShowBalloonTip(1, "", msg, ToolTipIcon.Info);
            }
        }

        #endregion

        public void ShowChart(string stockNo, ChartType chartType)
        {
            StockChart chart = new StockChart();
            chart.Show(stockNo, chartType);
        }

        #region 桌面 右键菜单

        Label stockLabel;

        private void timeSheetMenuItem_Click(object sender, EventArgs e)
        {
            if (stockLabel == null)
                return;
            this.ShowChart(stockLabel.Tag.ToString(), ChartType.TimeSheet);
        }
        private void DayKMenuItem_Click(object sender, EventArgs e)
        {
            if (stockLabel == null)
                return;
            this.ShowChart(stockLabel.Tag.ToString(), ChartType.KOfDay);
        }
        private void WeekKMenuItem_Click(object sender, EventArgs e)
        {
            if (stockLabel == null)
                return;
            this.ShowChart(stockLabel.Tag.ToString(), ChartType.KOfWeek);
        }
        private void MonthKMenuItem_Click(object sender, EventArgs e)
        {
            if (stockLabel == null)
                return;
            this.ShowChart(stockLabel.Tag.ToString(), ChartType.KOfMonth);
        }

        private void deskMenu_Opening(object sender, CancelEventArgs e)
        {
            stockLabel = (Label)(sender as ContextMenuStrip).SourceControl;
        }

        #endregion
        #region 通知区域  菜单

        private void topMostMenuItem_Click(object sender, EventArgs e)
        {
            TopMost = !TopMost;
        }
        private void MonitorMenuItem_Click(object sender, EventArgs e)
        {
            myStock.Show();
        }
        private void exitMenuItem_Click(object sender, EventArgs e)
        {
            forceClose = true;
            this.Close();
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (IsShow)
            {
                this.Hide();
            }
            else
            {
                this.Show();
            }
        }

        private void settingMenuItem_Click(object sender, EventArgs e)
        {
            SystemSetting setting = new SystemSetting();
            setting.Show();
        }
        private void noticeMenu_MouseClick(object sender, MouseEventArgs e)
        {
            this.Show();
        }

        #endregion

        #region 股票信息无关

        #region 窗体位置
        private void DeskStocks_ResizeEnd(object sender, EventArgs e)
        {
            SaveFormPosition();
        }
        #region 保存窗体位置信息和宽高信息，下次启动时加载

        private void SaveFormPosition()
        {
            //XDocument doc = XDocument.Load(fileName);
            //var startPositionX = doc.Descendants("startPosition").FirstOrDefault();
            //startPositionX.Attribute("top").Value = this.Top.ToString();
            //startPositionX.Attribute("left").Value = this.Left.ToString();
            //startPositionX.Attribute("width").Value = this.Width.ToString();
            //startPositionX.Attribute("height").Value = this.Height.ToString();
            //doc.Save(fileName);

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(fileName);

            var stockNode = xmlDoc.SelectSingleNode("/JStock");
            stockNode.Attributes["Balance"].Value = Constants.Balance.ToString();
            stockNode.Attributes["DeskDisplayFormat"].Value = Constants.DeskDisplayFormat;
            stockNode.Attributes["DBPath"].Value = Constants.DBPath;
            var startPositionX = xmlDoc.SelectSingleNode("/JStock/startPosition");
            startPositionX.Attributes["top"].Value = this.Top.ToString();
            startPositionX.Attributes["left"].Value = this.Left.ToString();
            startPositionX.Attributes["width"].Value = this.Width.ToString();
            startPositionX.Attributes["height"].Value = this.Height.ToString();
            xmlDoc.Save(fileName);
        }

        #endregion
        #region 加载上次窗体关闭时的问题
        private void LoadLastFormPosition()
        {
            #region XmlDocument

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(fileName);

            var stockNode = xmlDoc.SelectSingleNode("/JStock");
            string dbName = "JStock.s3db";


            if (!string.IsNullOrEmpty(stockNode.Attributes["Balance"].Value))
            {
                Constants.DBPath = stockNode.Attributes["DBPath"].Value;
                if (string.IsNullOrEmpty(Constants.DBPath))
                {
                    Constants.DBPath = Path.Combine(Environment.CurrentDirectory, dbName);
                }
                SqliteHelper.ConnStr = String.Format("Data Source={0};Version=3;Pooling=true;Max Pool Size=100;", Constants.DBPath);
                StockDAL.InitDB();
            }
            if (!string.IsNullOrEmpty(stockNode.Attributes["Balance"].Value))
            {
                Constants.Balance = decimal.Parse(stockNode.Attributes["Balance"].Value);
            }
            if (!string.IsNullOrEmpty(stockNode.Attributes["DeskDisplayFormat"].Value))
            {
                Constants.DeskDisplayFormat = stockNode.Attributes["DeskDisplayFormat"].Value;
            }

            var startPositionX = xmlDoc.SelectSingleNode("/JStock/startPosition");
            if (string.IsNullOrEmpty(startPositionX.Attributes["top"].Value) || string.IsNullOrEmpty(startPositionX.Attributes["left"].Value))
            {
                this.StartPosition = FormStartPosition.CenterScreen;
            }
            else
            {
                this.Top = int.Parse(startPositionX.Attributes["top"].Value);
                this.Left = int.Parse(startPositionX.Attributes["left"].Value);
            }
            if (!string.IsNullOrEmpty(startPositionX.Attributes["width"].Value))
            {
                this.Width = int.Parse(startPositionX.Attributes["width"].Value);
            }
            if (!string.IsNullOrEmpty(startPositionX.Attributes["height"].Value))
            {
                this.Height = int.Parse(startPositionX.Attributes["height"].Value);
            }

            #endregion

            #region Linq2XML

            //var startPositionX = doc.Descendants("startPosition").FirstOrDefault();
            //if (string.IsNullOrEmpty(startPositionX.Attribute("top").Value) || string.IsNullOrEmpty(startPositionX.Attribute("left").Value))
            //{
            //    this.StartPosition = FormStartPosition.CenterScreen;
            //}
            //else
            //{
            //    this.Top = int.Parse(startPositionX.Attribute("top").Value);
            //    this.Left = int.Parse(startPositionX.Attribute("left").Value);
            //}
            //if (!string.IsNullOrEmpty(startPositionX.Attribute("width").Value))
            //{
            //    this.Width = int.Parse(startPositionX.Attribute("width").Value);
            //}
            //if (!string.IsNullOrEmpty(startPositionX.Attribute("height").Value))
            //{
            //    this.Height = int.Parse(startPositionX.Attribute("height").Value);
            //}

            #endregion
        }

        #endregion
        #endregion
        #region 快捷键

        private void RegisterHotKey()
        {
            hotkeyHelper = new HotkeyHelper(this.Handle);
            showWindowKey = hotkeyHelper.RegisterHotkey(Keys.Oemtilde, HotkeyHelper.KeyFlags.MOD_CONTROL);
            showWarnKey = hotkeyHelper.RegisterHotkey(Keys.D1, HotkeyHelper.KeyFlags.MOD_CONTROL);
            hotkeyHelper.OnHotkey += new HotkeyEventHandler(OnHotkey);
        }
        private void OnHotkey(int hotkeyID)
        {
            if (hotkeyID == showWindowKey)
            {
                //do something
                if (IsShow)
                {
                    this.Hide();
                }
                else
                {
                    this.Show();
                }
            }
            else if (hotkeyID == showWarnKey)
            {
                Constants.ShowWarn = !Constants.ShowWarn;
            }
        }

        #endregion
        #region 鼠标滚轮 => 透明度

        void DeskStocks_MouseWheel(object sender, MouseEventArgs e)
        {

            if (e.Delta > 0 && this.Opacity < 1)
            {
                this.Opacity += 0.1;
            }
            else if (e.Delta < 0 && this.Opacity > 0.12)
            {
                this.Opacity -= 0.1;
            }

        }

        #endregion

        #endregion

        #region 覆盖系统方法

        private new void Show()
        {
            StockService.AddEvent(Display);

            IsShow = true;
            base.Show();
            this.WindowState = FormWindowState.Normal;
            this.Activate();
        }

        private new void Hide()
        {
            StockService.RemoveEvent(Display);

            base.Hide();
            IsShow = false;
        }

        #endregion

        private void CloseMe()
        {
            using (new JStopWatch().Start(LogMode.Info, "", "StockService.Stop"))
            {
                StockService.Stop();
            }
            using (new JStopWatch().Start(LogMode.Info, "", "StockService.ProcessEvent"))
            {
                StockService.RemoveEvent(Display);
                StockService.RemoveEvent(ShowWarn);
            }
            using (new JStopWatch().Start(LogMode.Info, "", "UnregisterHotkeys"))
            {
                hotkeyHelper.UnregisterHotkeys();
            }
            using (new JStopWatch().Start(LogMode.Info, "", "myStock.Close"))
            {
                myStock.Close(true);
            }
            using (new JStopWatch().Start(LogMode.Info, "", "this.Dispose"))
            {
                this.Dispose();
            }
            {
                Application.Exit();
            }
        }
    }

    public class FormInvokArgument
    {
        public TableLayoutPanel tableLayoutPanel1 { get; set; }
        public string Message { get; set; }
    }
}
